package jxtend.processor.eclipse.visitor.interceptor;

import jxtend.processor.eclipse.visitor.handler.impl.TypeUtils;
import jxtend.processor.reflect.ReflectMethod;
import jxtend.processor.reflect.ReflectMethod.ResultMethod2Args;
import jxtend.processor.reflect.ReflectMethod.ResultMethod3Args;

import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;

@SuppressWarnings("restriction")
public class FixParametersOfMethodBindingInterceptor extends ASTNodeVisitorInterceptorAdapter {
	private static final ResultMethod2Args<Scope, Boolean, TypeParameter[], Boolean> connectTypeVariablesMethod =
			ReflectMethod.method(Scope.class, Boolean.TYPE, "connectTypeVariables", TypeParameter[].class, Boolean.TYPE);

	private static final ResultMethod3Args<LookupEnvironment, ReferenceBinding, char[][], Boolean, Boolean>
		getTypeFromCompoundNameMethodOfLookupEnvironment = ReflectMethod.method(LookupEnvironment.class,
			ReferenceBinding.class, "getTypeFromCompoundName", char[][].class, boolean.class, boolean.class);

	@Override
	public boolean interceptVisit(ASTNodeVisitorInterceptorContext interceptorChain, ASTNode node, Scope scope) {
		if (node instanceof AbstractMethodDeclaration && ((AbstractMethodDeclaration) node).binding != null
				&& ((AbstractMethodDeclaration) node).arguments != null
				&& ((AbstractMethodDeclaration) node).arguments.length != 0
				&& ((AbstractMethodDeclaration) node).scope != null) {
			AbstractMethodDeclaration methodDeclaration = (AbstractMethodDeclaration) node;

			TypeBinding[] existingParameterTypeBindings = methodDeclaration.binding.parameters;
			TypeBinding[] newParameterTypeBindings = new TypeBinding[methodDeclaration.arguments.length];

			for (int i = 0; i < methodDeclaration.arguments.length; i++) {
				TypeBinding argumentTypeBinding =
					(existingParameterTypeBindings != null && i < existingParameterTypeBindings.length)
						? existingParameterTypeBindings[i] : null;

				if (argumentTypeBinding == null) {
					TypeReference typeReference = methodDeclaration.arguments[i].type;
//					ReflectField<TypeReference, TypeReference[]> typeArgumentsField = (ReflectField)
//						ReflectField.findPublicField(typeReference.getClass(), TypeReference[].class, "typeArguments");
//					if (typeAr)
//					connectTypeVariablesMethod.invoke(scope, new TypeParameter[] { (TypeParameter) currentNode }, true);

					if (typeReference instanceof ParameterizedSingleTypeReference) {


					}

//					TypeReference argumentType = methodDeclaration.arguments[i].type;
//					if (argumentType instanceof SingleTypeReference) {
//						char[] argumentTypeSignature = Signature.createCharArrayTypeSignature(
//								((SingleTypeReference) argumentType).token, false);
//						if (argumentTypeSignature[0] == 'Q') {
//							argumentTypeSignature[0] = argumentType instanceof Wildcard ? 'T' : 'L';
//						}
//						LookupEnvironment lookupEnvironment = scope.referenceCompilationUnit().scope.environment;
//
//						char[][][] missingTypeNames = new char[0][][];
//						ReferenceBinding referenceContextBinding = ((TypeDeclaration) scope.referenceContext()).binding;
//
//						TypeVariableBinding[] typeVariables = Binding.NO_TYPE_VARIABLES;
//
//						TypeParameter[] typeParameters = methodDeclaration.typeParameters();
//						if (typeParameters != null && typeParameters.length > 0) {
//							int length = typeParameters.length;
//							int count = 0;
//							typeVariables = new TypeVariableBinding[length];
//							for (int j = 0; j < length; j++) {
//								if (typeParameters[j].binding != null) {
//									typeVariables[count++] = typeParameters[j].binding;
//								}
//							}
//
////							if (count != length) {
////								System.arraycopy(typeVariables, 0, typeVariables = new TypeVariableBinding[count], 0, count);
////							}
//						}
//
//						argumentTypeBinding = lookupEnvironment.getTypeFromTypeSignature(
//								new SignatureWrapper(argumentTypeSignature), typeVariables,
//								referenceContextBinding, missingTypeNames);
//					} else {
						argumentTypeBinding =
								methodDeclaration.arguments[i].type.resolveType(methodDeclaration.scope, false);
//					}

//					argumentTypeBinding = TypeUtils.createUnresolvedTypeBinding(
//							scope, typeReference, methodDeclaration.typeParameters());
					
//					if (argumentType instanceof SingleTypeReference) {
//						
//
//						argumentTypeBinding = getTypeFromCompoundNameMethodOfLookupEnvironment.invoke(
//								lookupEnvironment, argumentType.getTypeName(),
//								(argumentType instanceof ParameterizedSingleTypeReference)
//								|| (argumentType instanceof ParameterizedQualifiedTypeReference),
//								false);
//						if (argumentType instanceof ArrayTypeReference
//								&& !(argumentType instanceof ParameterizedSingleTypeReference)) {
//							argumentTypeBinding =
//									new ArrayBinding(argumentTypeBinding, argumentType.dimensions(), lookupEnvironment);
//						} else if (argumentType instanceof ParameterizedSingleTypeReference) {
//							//TODO: don't now how to handle this; for now just call "resolveType"
//							argumentTypeBinding =
//									methodDeclaration.arguments[i].type.resolveType(methodDeclaration.scope, false);
//						}
//					} else {
//						argumentTypeBinding =
//								methodDeclaration.arguments[i].type.resolveType(methodDeclaration.scope, false);
//					}
				}

				newParameterTypeBindings[i] = argumentTypeBinding;
			}

			methodDeclaration.binding.parameters = newParameterTypeBindings;
		}

		return super.interceptVisit(interceptorChain, node, scope);
	}

}
